module BranchPrediction #(
    parameter DEPTH      = 64,
    parameter ADDR_WIDTH = 64,
    parameter STATE_NUM  = 2
) (
    input                   clk,
    input                   rst,
    input  [ADDR_WIDTH-1:0] pc_if,
    output logic            jump_if,
    output [ADDR_WIDTH-1:0] pc_target_if,

    input  [31:0]          inst_exe,
    input [ADDR_WIDTH-1:0] pc_exe,
    input [ADDR_WIDTH-1:0] pc_target_exe,
    input logic            jump_exe,   // 预测是否跳转
    input                  is_jump_exe // 实际是否跳转
);

    localparam INDEX_BEGIN = 2;
    localparam INDEX_LEN = $clog2(DEPTH);// log2,32 = 5
    localparam INDEX_END = INDEX_BEGIN + INDEX_LEN - 1; //6
    localparam TAG_BEGIN = INDEX_END + 1; //7
    localparam TAG_END = ADDR_WIDTH - 1; //63
    localparam TAG_LEN = TAG_END - TAG_BEGIN + 1; // 57

    typedef logic [TAG_LEN-1:0] tag_t;
    typedef logic [INDEX_LEN-1:0] index_t; 
    typedef logic [STATE_NUM-1:0] state_t; // 11强跳，10弱跳，01弱不跳，00强不跳
    typedef logic [ADDR_WIDTH-1:0] addr_t;

    typedef struct {
        tag_t   tag;   // [TAG_LEN-1:0]
        addr_t  target;// [ADDR_WIDTH-1:0]
        state_t state; // [STATE_NUM-1:0]
        logic   valid; // 1 bit
    } BTBLine;

    BTBLine btb       [DEPTH-1:0];

    tag_t   tag_exe;
    index_t index_exe;
    BTBLine btb_exe;
    assign tag_exe   = pc_exe[TAG_END:TAG_BEGIN]; //63:7
    assign index_exe = pc_exe[INDEX_END:INDEX_BEGIN]; //6:2
    assign btb_exe   = btb[index_exe];


    tag_t   tag_if;
    index_t index_if;
    BTBLine btb_if;
    assign tag_if   = pc_if[TAG_END:TAG_BEGIN];
    assign index_if = pc_if[INDEX_END:INDEX_BEGIN];
    assign btb_if   = btb[index_if];

    assign jump_if = btb_if.valid & (btb_if.state==2'b11 || btb_if.state==2'b10)  & (btb_if.tag==tag_if);
    assign pc_target_if = (jump_if)?btb_if.target:pc_if; // 如果预测跳转，target设置为btb中target,如果预测不跳转，则正常处理
    // wire EXE_jump = btb_exe.valid & (btb_exe.state==2'b11 || btb_exe.state==2'b10);
    always @(posedge clk or posedge rst) begin
                if(is_jump_exe)  begin // 如果跳转，则强化跳转并将PC作为表中target
                    case(btb_exe.state)
                        2'b11: btb[index_exe].state <= 2'b11;
                        2'b10: btb[index_exe].state <= 2'b11;
                        2'b01: btb[index_exe].state <= 2'b11;
                        2'b00: btb[index_exe].state <= 2'b01;
                    endcase
                    if(inst_exe!=32'h00008067 && inst_exe !=32'h30200073  && inst_exe !=32'h10200073) begin  // 相同的 ret , sret 和 mret 指令可能跳转地址不同，不作记录
                        btb[index_exe].target <= pc_target_exe;
                        btb[index_exe].tag <= tag_exe;
                        btb[index_exe].valid <= 1'b1;
                    end
                end
                else begin // 如果不跳转，则强化不跳转条件
                    case(btb_exe.state)
                        2'b11: btb[index_exe].state <= 2'b10;
                        2'b10: btb[index_exe].state <= 2'b00;
                        2'b01: btb[index_exe].state <= 2'b00;
                        2'b00: btb[index_exe].state <= 2'b00;
                    endcase
                    btb[index_exe].valid <= 1'b1;
                end
    end

endmodule